package com.example.service;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class DbConnectionServiceTest {

    @InjectMocks
    private DbConnectionService dbConnectionService;

    @Test
    void testConnectionSuccess() throws SQLException {
        // 模拟成功的数据库连接
        try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
            Connection mockConnection = mock(Connection.class);
            mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                    .thenReturn(mockConnection);

            // 执行测试
            assertDoesNotThrow(() -> 
                dbConnectionService.testConnection("jdbc:mysql://localhost:3306/test", "root", "password")
            );

            // 验证调用
            mockedDriverManager.verify(() -> 
                DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password")
            );
        }
    }

    @Test
    void testConnectionAuthenticationFailure() {
        // 模拟认证失败
        try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
            SQLException authException = new SQLException("Access denied for user 'root'@'localhost'", "28000", 1045);
            mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                    .thenThrow(authException);

            // 执行测试并验证异常
            SQLException exception = assertThrows(SQLException.class, () ->
                dbConnectionService.testConnection("jdbc:mysql://localhost:3306/test", "wronguser", "wrongpass")
            );

            assertEquals("28000", exception.getSQLState());
            assertEquals(1045, exception.getErrorCode());
            assertTrue(exception.getMessage().contains("Access denied"));
        }
    }

    @Test
    void testConnectionHostUnreachable() {
        // 模拟主机不可达
        try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
            SQLException connectionException = new SQLException("Communications link failure", "08001", 0);
            mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                    .thenThrow(connectionException);

            // 执行测试并验证异常
            SQLException exception = assertThrows(SQLException.class, () ->
                dbConnectionService.testConnection("jdbc:mysql://nonexistent:3306/test", "root", "password")
            );

            assertEquals("08001", exception.getSQLState());
            assertEquals(0, exception.getErrorCode());
            assertTrue(exception.getMessage().contains("Communications link failure"));
        }
    }

    @Test
    void testConnectionUnknownDatabase() {
        // 模拟数据库不存在
        try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
            SQLException dbException = new SQLException("Unknown database 'nonexistent'", "42000", 1049);
            mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                    .thenThrow(dbException);

            // 执行测试并验证异常
            SQLException exception = assertThrows(SQLException.class, () ->
                dbConnectionService.testConnection("jdbc:mysql://localhost:3306/nonexistent", "root", "password")
            );

            assertEquals("42000", exception.getSQLState());
            assertEquals(1049, exception.getErrorCode());
            assertTrue(exception.getMessage().contains("Unknown database"));
        }
    }

    @Test
    void testConnectionTimeout() {
        // 模拟连接超时
        try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
            SQLException timeoutException = new SQLException("Connection timed out", "08S01", 0);
            mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                    .thenThrow(timeoutException);

            // 执行测试并验证异常
            SQLException exception = assertThrows(SQLException.class, () ->
                dbConnectionService.testConnection("jdbc:mysql://localhost:3306/test", "root", "password")
            );

            assertEquals("08S01", exception.getSQLState());
            assertEquals(0, exception.getErrorCode());
            assertTrue(exception.getMessage().contains("Connection timed out"));
        }
    }

    @Test
    void testConnectionRefused() {
        // 模拟连接被拒绝
        try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
            SQLException refusedException = new SQLException("Connection refused", "08S01", 0);
            mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                    .thenThrow(refusedException);

            // 执行测试并验证异常
            SQLException exception = assertThrows(SQLException.class, () ->
                dbConnectionService.testConnection("jdbc:mysql://localhost:3306/test", "root", "password")
            );

            assertEquals("08S01", exception.getSQLState());
            assertEquals(0, exception.getErrorCode());
            assertTrue(exception.getMessage().contains("Connection refused"));
        }
    }

    @Test
    void testConnectionBadHandshake() {
        // 模拟握手失败
        try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
            SQLException handshakeException = new SQLException("Bad handshake", "08S01", 0);
            mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                    .thenThrow(handshakeException);

            // 执行测试并验证异常
            SQLException exception = assertThrows(SQLException.class, () ->
                dbConnectionService.testConnection("jdbc:mysql://localhost:3306/test", "root", "password")
            );

            assertEquals("08S01", exception.getSQLState());
            assertEquals(0, exception.getErrorCode());
            assertTrue(exception.getMessage().contains("Bad handshake"));
        }
    }

    @Test
    void testConnectionWithNullParameters() {
        // 测试空参数
        assertThrows(SQLException.class, () ->
            dbConnectionService.testConnection(null, null, null)
        );
    }

    @Test
    void testConnectionWithEmptyParameters() {
        // 测试空字符串参数
        try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
            SQLException invalidException = new SQLException("Invalid connection parameters", "HY000", 0);
            mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                    .thenThrow(invalidException);

            assertThrows(SQLException.class, () ->
                dbConnectionService.testConnection("", "", "")
            );
        }
    }
} 